package org.jinyan.led.tcp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.jinyan.led.constant.TCPConstants;
import org.jinyan.led.model.ClientSocket;
import org.jinyan.led.model.DeviceInstance;
import org.jinyan.led.service.DeviceService;
import org.jinyan.utils.LogSysoutUtils;

/**
 * 
 * @author jinyan
 * @version 1.0
 * 
 */

public class LedServerSocket {

	private static ServerSocket ss = null;
	private static ServerSocketChannel ssc = null;
	private static Selector selector = null;
	private InetSocketAddress address = null;
	private boolean isClosed = false;

	public void todoSocket(Selector selector) {

		// 绑定服务端口，并定义一个事件选择器对象记录套接字通道的事件

		/* 通过此循环来遍例事件 */
		while (true && !isClosed) {
			// LogSysoutUtils.log("Waiting events.");
			int readyChannels = 0;
			try {
				readyChannels = selector.select();
				// LogSysoutUtils.log("Got events: " + readyChannels);
			} catch (IOException e1) {
				isClosed = true;
				LogSysoutUtils.log(" start IOException 1" + e1.toString());
				e1.printStackTrace();
			} // 查询事件如果一个事件都没有，这里就会阻塞

			if (readyChannels == 0)
				continue;
			ByteBuffer echoBuffer = ByteBuffer.allocate(125); // 定义一个byte缓冲区来存储收发的数据
			/* 循环遍例所有产生的事件 */

			Set<SelectionKey> selectedKeys = selector.selectedKeys(); // 获取发生的事件
			Iterator<SelectionKey> it = selectedKeys.iterator(); // 依次进行处理
			while (it.hasNext()) {
				SelectionKey tempKey = it.next();
				SocketChannel sc = null;
				/* 如果产生的事件为接受客户端连接(当有客户端连接服务器的时候产生) */
				if (tempKey.isValid() && tempKey.isAcceptable()) {
					ServerSocketChannel subssc = (ServerSocketChannel) tempKey
							.channel(); // 定义一个服务器socket通道
					try {
						sc = subssc.accept();
						sc.configureBlocking(false);
						sc.register(selector, SelectionKey.OP_READ);
					} catch (IOException e) {
						isClosed = true;
						LogSysoutUtils.log(" start IOException 2"
								+ e.toString());
						e.printStackTrace();
					} // 将临时socket对象实例化为接收到的客户端的socket
					String success = TCPConstants.OPEN;
					writeBack(sc, success.getBytes()); // 向客户端写入收到的数据
				} else if (tempKey.isValid() && tempKey.isReadable()) {
					/*
					 * 如果产生的事件为读取数据( 当已连接的客户端向服务器发送数据的时候产生 )
					 */
					String returnStr = TCPConstants.SUCCESS;
					sc = (SocketChannel) tempKey.channel(); // 临时socket对象实例化为产生本事件的socket
					ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 定义一个用于存储byte数据的流对象，存储全部信息
					String reciveMsg = null;
					echoBuffer.clear(); // 先将客户端的数据清空

					int readInt = 0; // 为读取到数据的长度
					try {
						if (sc.isConnected()) {
							while ((readInt = sc.read(echoBuffer)) > 0) {
								// 如果获得数据长度比缓冲区大小小的话
								if (readInt < echoBuffer.capacity()) {
									byte[] readByte = new byte[readInt]; // 建立一个临时byte数组,将齐长度设为获取的数据的长度
									// 循环向此临时数组中添加数据
									for (int i = 0; i < readInt; i++) {
										readByte[i] = echoBuffer.get(i);
									}
									bos.write(readByte); // 将此数据存入byte流中
								}
								// 否则就是获得数据长度等于缓冲区大小
								else {
									bos.write(echoBuffer.array()); // 将读取到的数据写入到byte流对象中
								}
							}
							// 当循环结束时byte流中已经存储了客户端发送的所有byte数据
							reciveMsg = new String(bos.toByteArray());
							System.out.println("reciveMsg:" + reciveMsg);
							String serialNumber = reciveMsg;
							SocketAddress clientAddress = sc.socket()
									.getRemoteSocketAddress();
							String ip = clientAddress.toString();
							String port = sc.socket().getLocalPort() + "";
							if (clientAddress != null) {
								ip = ip.substring(1, ip.length());
								String[] ipAndPort = ip.split(":");
								ip = ipAndPort[0];
								port = ipAndPort[1];
							}
							System.out.println("ip:" + ip + "port:" + port);
							String hostName = sc.socket().getInetAddress()
									.getHostName();
							if (serialNumber != null
									&& !"".equals(serialNumber)) {
								DeviceService deviceService = new DeviceService();
								DeviceInstance instance = deviceService
										.getDeviceInstance(serialNumber);
								if (instance != null) {
									instance.setPort(port);
									instance.setIsConnect("Y");
									instance.setIp(ip);
									instance.setHostName(hostName);
									deviceService
											.updateDeviceInstance(instance);
									deviceService
											.saveDeviceInstanceHis(instance);
									ClientSocket client = new ClientSocket();
									client.setId(serialNumber);
									client.setDeviceInstance(instance);
									client.setSocketChannel(sc);
									System.out.println("Got new client:" + sc);
									ClientSocketManager.putClientSocket(
											client.getId(), client);
								} else {
									returnStr = TCPConstants.FAIL;
								}
							}
						} else {
							returnStr = TCPConstants.CLOSE;
						}
						LogSysoutUtils.log("Recive msg: " + reciveMsg);
						if (readInt < 0) {
							sc.close();
							ClientSocketManager.clearClientSocket(sc);
						} else {
							writeBack(sc, returnStr.getBytes()); // 向客户端写入收到的数据
						}
					} catch (IOException e) {
						LogSysoutUtils.log(" start  IOException 3 "
								+ e.toString());
						e.printStackTrace(); // 当客户端在读取数据操作执行之前断开连接会产生异常信息
						tempKey.cancel(); // 将本socket的事件在选择器中删除 break; }
					}

				} else if (tempKey.isValid() && tempKey.isWritable()) {
					System.out.println("Writable");
				} else if (tempKey.isValid() && tempKey.isConnectable()) {
					System.out.println("isConnectable");
				}
				it.remove();
				selector.selectedKeys().remove(tempKey);
			}

		}

	}

	/**
	 * 绑定服务端口，初始化整个服务
	 * 
	 * @throws IOException
	 */
	private Selector bindServer(int portNum) {
		LogSysoutUtils.log("Start binding server socket：" + portNum);

		try {
			if (selector == null) {
				selector = Selector.open();
				ssc = ServerSocketChannel.open();
			}
			ssc.configureBlocking(false);
			ss = ssc.socket(); // 定义服务器socket对象-用来指定异步socket的监听端口等信息
			if (address == null) {
				address = new InetSocketAddress(portNum); // 定义存放监听端口的对象
				ss.bind(address);
				ss.setReuseAddress(false);
			}
			isClosed = false;
		} catch (IOException e) {
			LogSysoutUtils.log("Binded socket IOException " + e.toString());
			e.printStackTrace();
		} // 定义一个事件选择器对象记录套接字通道的事件

		try {
			ssc.register(selector, SelectionKey.OP_ACCEPT);
		} catch (ClosedChannelException e) {
			isClosed = true;
			LogSysoutUtils.log("Binded socket ClosedChannelException "
					+ e.toString());
			e.printStackTrace();
		} // 将异步的服务器socket对象的接受客户端连接事件注册到selector对象内

		LogSysoutUtils.log("Binded socket at：" + portNum);

		return selector;
	}

	private static boolean writeBack(SocketChannel sc, byte[] b) {
		ByteBuffer echoBuffer = ByteBuffer.allocate(b.length); // 建立这个byte对象的ByteBuffer
		echoBuffer.put(b); // 将数据存入

		echoBuffer.flip(); // 将缓冲区复位以便于进行其他读写操作
		try {
			// 向客户端写入数据,数据为接受到数据
			if (sc.isConnected()) {
				System.out.println("Msg echo back: "
						+ new String(echoBuffer.array()));
				sc.write(echoBuffer);
			}

		} catch (IOException e) {

			LogSysoutUtils.log(" writeBack IOException " + e.toString());
			e.printStackTrace();
			return false;
		}

		return true;
	}

	public boolean isClose() {
		if (ss == null || ssc == null || isClosed) {
			return true;
		} else {
			return false;
		}
	}

	public void restart(int portNum) {
		selector = bindServer(portNum);
		isClosed = false;
		// this.initSocket(selector);
		this.todoSocket(selector);
	}

	private void initSocket(Selector selector2) {
		DeviceService deviceService = new DeviceService();
		List<DeviceInstance> deviceList = deviceService.getAllDeviceInstance();
		for (int i = 0; i < deviceList.size(); i++) {
			DeviceInstance instance = deviceList.get(i);
			String ip = instance.getIp();
			String port = instance.getPort();

		}

	}

	public static String operateDevice(ClientSocket client, String op) {
		System.out.println("操作LED start " + client);
		String result = "";
		if (client == null) {
			return result = "服务端与LED没有连接成功";
		}
		DeviceInstance led = client.getDeviceInstance();
		String id = led.getId();

		SocketChannel scTemp = client.getSocketChannel();
		if ("OPEN".equals(op)) {
			writeBack(scTemp, TCPConstants.OP_OPEN_LED_CMD.getBytes());
			result = "打开[" + led.getAlias() + "]成功";
		} else if ("CLOSE".equals(op)) {
			writeBack(scTemp, TCPConstants.OP_CLOSE_LED_CMD.getBytes());
			result = "关闭[" + led.getAlias() + "]成功";
		}
		System.out.println("操作LED end " + result);
		return result;
	}

}
